package site.shihuan.helpdesk.im;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import site.shihuan.helpdesk.common.cache.Cache;
import site.shihuan.helpdesk.common.model.JwtUser;
import site.shihuan.helpdesk.im.message.BaseMessage;
import site.shihuan.helpdesk.im.message.impl.ContactMessage;
import site.shihuan.helpdesk.im.manager.ClerkManager;
import site.shihuan.helpdesk.im.manager.VisitorManager;
import site.shihuan.helpdesk.im.model.Constants;
import site.shihuan.helpdesk.im.model.po.SvcVisitorPO;
import site.shihuan.helpdesk.im.model.vo.ClerkVO;

import javax.annotation.PreDestroy;
import javax.websocket.Session;
import java.io.IOException;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

/**
 * SessionContextHolder.
 *
 * @author 周世焕
 * @Date 2021/2/2 19:49
 */
@Slf4j
@Component
public class SessionContextHolder {

    @Autowired
    private VisitorManager visitorManager;

    @Autowired
    private ClerkManager clerkManager;

    /**
     * 用户ID与Session（用于数据传输的会话）的映射
     */
    private static ConcurrentHashMap<String, List<Session>> user2session = new ConcurrentHashMap<>();

    /**
     * Session ID与用户ID（用于数据传输的会话）的映射
     */
    private static ConcurrentHashMap<String, JwtUser> session2user = new ConcurrentHashMap<>();

    /**
     * 记录访客上线
     * @param jwtUser
     * @param session
     */
    public void addVisitor(JwtUser jwtUser,Session session) {
        addSessionToMap(jwtUser,session);
        SvcVisitorPO visitorPO = visitorManager.findVisitor(jwtUser.getTenantId(),jwtUser.getUid());
        //该客户端第一次访问
        if(visitorPO == null || !visitorPO.getTenantId().equals(jwtUser.getTenantId())) {
            visitorPO = visitorManager.createVisitor(jwtUser.getUid(),jwtUser.getTenantId());
        }else {
            visitorManager.changeOnlineStatus(jwtUser.getUid(),jwtUser.getTenantId(),Constants.STATUS_ONLINE);
        }
        ContactMessage contactMessage = new ContactMessage();
        if(Constants.VISITOR_WAIT_STATE.equals(visitorPO.getAccept())) {
            //访客新增通知
            contactMessage.setCmd(ContactMessage.CMD_ADD_VISITOR);
            contactMessage.setPayload(visitorPO);
        }else {
            //已接入的访客上线通知
            contactMessage.setCmd(ContactMessage.CMD_ONLINE_NOTIFY);
            contactMessage.setPayload(visitorPO.getVisitorId());
        }
        broadcastAllClerk(contactMessage,jwtUser.getTenantId());
    }

    /**
     * 记录客服用户上线
     * @param jwtUser
     * @param session
     */
    public void addUser(JwtUser jwtUser,Session session) {
        clerkManager.changeOnlineStatus(jwtUser.getUid(),Constants.STATUS_ONLINE);
        addSessionToMap(jwtUser,session);
    }

    /**
     * 记录Session
     * @param user
     * @param session
     */
    private void addSessionToMap(JwtUser user,Session session) {
        List<Session> sessions = user2session.get(user.getUid());
        if(sessions == null) {
            sessions = new LinkedList<>();
            user2session.put(user.getUid(),sessions);
        }
        sessions.add(session);
        session2user.put(session.getId(),user);
    }

    private void removeUserFromMap(JwtUser user,Session session) {
        List<Session> sessions = user2session.get(user.getUid());
        if(sessions == null) {
            return;
        }
        sessions.remove(session);
        if(sessions.size() == 0) {
            user2session.remove(user.getUid());
        }
    }

    public void remove(Session session) {
        JwtUser user = getUser(session);
        if(user != null){
            removeUserFromMap(user,session);
            if(user2session.get(user.getUid()) == null) {
                if (user.getRole() != null && user.getRole().equals(site.shihuan.helpdesk.common.model.Constants.ROLE_VISITOR)) {
                    visitorManager.changeOnlineStatus(user.getUid(), user.getTenantId(), Constants.STATUS_OFFLINE);
                    ContactMessage contactMessage = new ContactMessage();
                    contactMessage.setCmd(ContactMessage.CMD_OFFLINE_NOTIFY);
                    contactMessage.setPayload(user.getUid());
                    broadcastAllClerk(contactMessage, user.getTenantId());
                } else {
                    clerkManager.changeOnlineStatus(user.getUid(), Constants.STATUS_OFFLINE);
                }
            }
        }
        session2user.remove(session.getId());
    }

    public JwtUser getUser(Session session) {
        return session2user.get(session.getId());
    }

    public String getUserId(Session session) {
        return session2user.get(session.getId()).getUid();
    }

    public String getTenantId(Session session) {
        return session2user.get(session.getId()).getTenantId();
    }

    public void sendMessage(BaseMessage message, String receiver){
        List<Session> sessions = user2session.get(receiver);
        if(sessions != null) {
            sessions.forEach(session -> {
                if(session.isOpen()) {
                    try {
                        session.getBasicRemote().sendText(message.encode());
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            });
        }else{
            //TODO 对方不在线则将消息发到消息队列，直到对方上线
            log.debug("消息未发送，原因：{}不在线",receiver);
        }
    }

    public void sendMessage(BaseMessage message, Session session){
        try {
            session.getBasicRemote().sendText(message.encode());
        } catch (IOException e) {
            log.debug("sendMessage失败：{}",e.getMessage());
        }
    }

    /**
     * 广播给所有客服人员(Clerk)
     * @param message
     * @throws IOException
     */
    public void broadcastAllClerk(BaseMessage message,String tenantId) {
        for(String uid: clerkManager.findOnlineClerkIds(tenantId)) {
            sendMessage(message,uid);
        }
    }

    /**
     * 程序结束时，下线该程序连接的所有用户
     */
    @PreDestroy
    public void exit(){
        Set set =  user2session.keySet();
        clerkManager.changeOnlineStatusBatch(set, Constants.STATUS_OFFLINE);
        visitorManager.changeOnlineStatusBatch(set, Constants.STATUS_OFFLINE);
    }
}
